1 module hip.hiprenderer.backend.metal.mtltexture; 2 version(AppleOS): 3 import metal; 4 import hip.hiprenderer; 5 import hip.hiprenderer.backend.metal.mtlrenderer; 6 import hip.console.log; 7 import metal.texture; 8 9 10 MTLPixelFormat getPixelFormat(in IImage img) 11 { 12 final switch(img.getBytesPerPixel) 13 { 14 case 1: return MTLPixelFormat.R8Unorm; 15 case 4: return MTLPixelFormat.RGBA8Unorm; 16 case 2: 17 case 3: 18 } 19 assert(0); 20 } 21 22 23 MTLSamplerAddressMode fromHipTextureWrapMode(TextureWrapMode m) 24 { 25 final switch(m) 26 { 27 case TextureWrapMode.CLAMP_TO_EDGE: 28 return MTLSamplerAddressMode.ClampToEdge; 29 case TextureWrapMode.MIRRORED_CLAMP_TO_EDGE: 30 return MTLSamplerAddressMode.MirrorClampToEdge; 31 case TextureWrapMode.REPEAT: 32 return MTLSamplerAddressMode.Repeat; 33 case TextureWrapMode.MIRRORED_REPEAT: 34 return MTLSamplerAddressMode.MirrorRepeat; 35 case TextureWrapMode.CLAMP_TO_BORDER: 36 return MTLSamplerAddressMode.ClampToBorderColor; 37 case TextureWrapMode.UNKNOWN: assert(false, "Don't use that"); 38 39 } 40 } 41 42 class HipMTLTexture : IHipTexture 43 { 44 MTLTextureDescriptor desc; 45 MTLTexture texture; 46 MTLDevice device; 47 MTLSamplerState sampler; 48 MTLSamplerDescriptor samplerDesc = null; 49 50 HipMTLRenderer mtlRenderer; 51 MTLCommandQueue cmdQueue; 52 53 uint width, height; 54 55 IHipTexture getBackendHandle(){return this;} 56 57 this(MTLDevice device, MTLCommandQueue cmdQueue, HipMTLRenderer mtlRenderer) 58 { 59 this.device = device; 60 this.cmdQueue = cmdQueue; 61 this.mtlRenderer = mtlRenderer; 62 samplerDesc = MTLSamplerDescriptor.alloc.initialize; 63 64 setWrapMode(TextureWrapMode.REPEAT); 65 setTextureFilter(TextureFilter.NEAREST, TextureFilter.NEAREST); 66 } 67 68 void setWrapMode(TextureWrapMode mode) 69 { 70 MTLSamplerAddressMode wrap = mode.fromHipTextureWrapMode; 71 samplerDesc.rAddressMode = wrap; 72 samplerDesc.sAddressMode = wrap; 73 samplerDesc.tAddressMode = wrap; 74 if(sampler) sampler.release(); 75 sampler = device.newSamplerStateWithDescriptor(samplerDesc); 76 } 77 78 void setTextureFilter(TextureFilter min, TextureFilter mag) 79 { 80 final switch ( min ) with(TextureFilter) 81 { 82 case LINEAR: 83 samplerDesc.minFilter = MTLSamplerMinMagFilter.Linear; 84 samplerDesc.mipFilter = MTLSamplerMipFilter.NotMipmapped; 85 break; 86 case NEAREST: 87 samplerDesc.minFilter = MTLSamplerMinMagFilter.Nearest; 88 samplerDesc.mipFilter = MTLSamplerMipFilter.NotMipmapped; 89 break; 90 case NEAREST_MIPMAP_NEAREST: 91 samplerDesc.minFilter = MTLSamplerMinMagFilter.Nearest; 92 samplerDesc.mipFilter = MTLSamplerMipFilter.Nearest; 93 break; 94 case LINEAR_MIPMAP_NEAREST: 95 samplerDesc.minFilter = MTLSamplerMinMagFilter.Linear; 96 samplerDesc.mipFilter = MTLSamplerMipFilter.Nearest; 97 break; 98 case NEAREST_MIPMAP_LINEAR: 99 samplerDesc.minFilter = MTLSamplerMinMagFilter.Nearest; 100 samplerDesc.mipFilter = MTLSamplerMipFilter.Linear; 101 break; 102 case LINEAR_MIPMAP_LINEAR: 103 samplerDesc.minFilter = MTLSamplerMinMagFilter.Linear; 104 samplerDesc.mipFilter = MTLSamplerMipFilter.Linear; 105 break; 106 } 107 final switch ( mag ) with(TextureFilter) 108 { 109 case LINEAR: 110 samplerDesc.magFilter = MTLSamplerMinMagFilter.Linear; 111 samplerDesc.mipFilter = MTLSamplerMipFilter.NotMipmapped; 112 break; 113 case NEAREST: 114 samplerDesc.magFilter = MTLSamplerMinMagFilter.Nearest; 115 samplerDesc.mipFilter = MTLSamplerMipFilter.NotMipmapped; 116 break; 117 case NEAREST_MIPMAP_NEAREST: 118 samplerDesc.magFilter = MTLSamplerMinMagFilter.Nearest; 119 samplerDesc.mipFilter = MTLSamplerMipFilter.Nearest; 120 break; 121 case LINEAR_MIPMAP_NEAREST: 122 samplerDesc.magFilter = MTLSamplerMinMagFilter.Linear; 123 samplerDesc.mipFilter = MTLSamplerMipFilter.Nearest; 124 break; 125 case NEAREST_MIPMAP_LINEAR: 126 samplerDesc.magFilter = MTLSamplerMinMagFilter.Nearest; 127 samplerDesc.mipFilter = MTLSamplerMipFilter.Linear; 128 break; 129 case LINEAR_MIPMAP_LINEAR: 130 samplerDesc.magFilter = MTLSamplerMinMagFilter.Linear; 131 samplerDesc.mipFilter = MTLSamplerMipFilter.Linear; 132 break; 133 } 134 if(sampler) sampler.release(); 135 sampler = device.newSamplerStateWithDescriptor(samplerDesc); 136 } 137 138 private static bool textureToSquare(out ubyte[] ret, const ubyte[] textureData, uint width, uint height) 139 { 140 uint nWidth = width; 141 if(nWidth < height) nWidth = height; 142 else return false; 143 ret = new ubyte[nWidth*height]; 144 145 for(size_t i = 0; i < height; i++) 146 ret[i*nWidth..i*nWidth+width] = textureData[i*width..(i+1)*width]; 147 return true; 148 } 149 150 protected bool loadImpl(in IImage img) 151 { 152 desc = MTLTextureDescriptor.alloc.initialize; 153 desc.pixelFormat = getPixelFormat(img); 154 desc.width = img.getWidth(); 155 desc.height = img.getHeight(); 156 desc.textureType = MTLTextureType._2D; 157 desc.storageMode = MTLStorageMode.Private; 158 159 if(desc is null) 160 return false; 161 162 width = img.getWidth; 163 height = img.getHeight; 164 165 const ubyte[] data = img.getPixels; 166 ubyte[] squareData; 167 168 MTLCommandBuffer b = cmdQueue.defaultCommandBuffer(); 169 MTLBlitCommandEncoder blit = b.blitCommandEncoder(); 170 MTLBuffer imageBuffer; 171 NSUInteger bytesPerRow; 172 NSUInteger bytesPerImage; 173 174 texture = device.newTextureWithDescriptor(desc); 175 176 if(textureToSquare(squareData, data, img.getWidth, img.getHeight)) 177 { 178 assert(img.getHeight > img.getWidth); 179 imageBuffer = device.newBuffer(squareData.ptr, squareData.length, MTLResourceOptions.StorageModeShared); 180 bytesPerRow = img.getHeight * img.getBytesPerPixel; 181 bytesPerImage = squareData.length; 182 } 183 else 184 { 185 imageBuffer = device.newBuffer(img.getPixels.ptr, img.getPixels.length, MTLResourceOptions.StorageModeShared); 186 bytesPerRow = img.getWidth * img.getBytesPerPixel; 187 bytesPerImage = img.getPixels.length; 188 } 189 blit.copyFromBuffer( 190 imageBuffer, 0, bytesPerRow, bytesPerImage, 191 MTLSize(desc.width, desc.height, 1), 192 texture, 0, 0, MTLOrigin(0,0,0) 193 ); 194 blit.optimizeContentsForGPUAccess(texture); 195 blit.endEncoding(); 196 b.commit(); 197 b.waitUntilCompleted(); 198 if(b.error) 199 NSLog("Command Buffer Error: %@".ns, b.error); 200 // imageBuffer.dealloc(); 201 if(squareData.ptr !is null) 202 { 203 import core.memory; 204 GC.free(squareData.ptr); 205 } 206 207 return texture !is null; 208 } 209 210 void bind(int slot = 0) 211 { 212 mtlRenderer.getEncoder.setFragmentSamplerState(sampler, slot); 213 mtlRenderer.getEncoder.setFragmentTexture(texture, slot); 214 } 215 216 void unbind(int slot = 0) 217 { 218 mtlRenderer.getEncoder.setFragmentSamplerState(null, slot); 219 mtlRenderer.getEncoder.setFragmentTexture(null, slot); 220 } 221 bool hasSuccessfullyLoaded(){return width != 0;} 222 int getWidth() const{return width;} 223 int getHeight() const{return height;} 224 }